/**
 * 
 */
package com.googlecode.dynargs.extractor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import com.googlecode.dynargs.model.constraints.Constraint;

/**
 * @author Federico De Faveri defaveri@gmail.com
 *
 */
public class ValidationConstraintsExtractor implements ConstraintExtractor {

	protected Map<Class<? extends Annotation>, ConstraintConverter<? extends Annotation>> converters;

	public ValidationConstraintsExtractor()
	{
		converters = new HashMap<Class<? extends Annotation>, ValidationConstraintsExtractor.ConstraintConverter<? extends Annotation>>();
		registerDefaults();
	}

	protected <T extends Annotation> void registerConverter(Class<T> clazz, ConstraintConverter<T> converter)
	{
		converters.put(clazz, converter);
	}

	protected void registerDefaults()
	{
		ConstraintConverter<NotNull> notNullConverter = new ConstraintConverter<NotNull>() {

			@Override
			public Constraint convert(NotNull annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.NotNull(annotation.message());
			}
		};
		registerConverter(NotNull.class, notNullConverter);

		ConstraintConverter<Min> minConverter = new ConstraintConverter<Min>() {

			@Override
			public Constraint convert(Min annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.Min(annotation.message(), annotation.value());
			}
		};
		registerConverter(Min.class, minConverter);

		ConstraintConverter<Max> maxConverter = new ConstraintConverter<Max>() {

			@Override
			public Constraint convert(Max annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.Max(annotation.message(), annotation.value());
			}
		};
		registerConverter(Max.class, maxConverter);

		ConstraintConverter<Past> pastConverter = new ConstraintConverter<Past>() {

			@Override
			public Constraint convert(Past annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.Past(annotation.message());
			}
		};
		registerConverter(Past.class, pastConverter);

		ConstraintConverter<Future> futureConverter = new ConstraintConverter<Future>() {

			@Override
			public Constraint convert(Future annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.Future(annotation.message());
			}
		};
		registerConverter(Future.class, futureConverter);

		ConstraintConverter<Pattern> patternConverter = new ConstraintConverter<Pattern>() {

			@Override
			public Constraint convert(Pattern annotation) {

				String[] flags = new String[annotation.flags()!=null?annotation.flags().length:0];
				if (annotation.flags()!=null) for (int i = 0; i < flags.length; i++) flags[i] = annotation.flags()[i].toString();
				return new com.googlecode.dynargs.model.constraints.validation.Pattern(annotation.message(), annotation.regexp(), flags);
			}
		};
		registerConverter(Pattern.class, patternConverter);

		ConstraintConverter<Size> sizeConverter = new ConstraintConverter<Size>() {

			@Override
			public Constraint convert(Size annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.Size(annotation.message(), annotation.min(), annotation.max());
			}
		};
		registerConverter(Size.class, sizeConverter);

		ConstraintConverter<Digits> digitsConverter = new ConstraintConverter<Digits>() {

			@Override
			public Constraint convert(Digits annotation) {
				return new com.googlecode.dynargs.model.constraints.validation.Digits(annotation.message(), annotation.fraction(), annotation.integer());
			}
		};
		registerConverter(Digits.class, digitsConverter);
	}

	/**
	 * {@inheritDoc}
	 */
	 @Override
	 public boolean canExtract(Field field, Annotation annotation) {
		 return converters.containsKey(annotation.annotationType());
	 }

	 @SuppressWarnings("unchecked")
	 protected <T extends Annotation> Constraint convert(T annotation)
	 {
		 ConstraintConverter<T> converter = (ConstraintConverter<T>) converters.get(annotation.annotationType());
		 return converter.convert(annotation);
	 }

	 /**
	  * {@inheritDoc}
	  */
	 @Override
	 public Constraint extract(Field field, Annotation annotation) {
		 return convert(annotation);
	 }

	 protected interface ConstraintConverter<T extends Annotation> {
		 public Constraint convert(T annotation);
	 }

}
